/******************************************************************************
 * This file is part of libemb.
 *
 * libemb is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * libemb is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with libemb.  If not, see <http://www.gnu.org/licenses/>.
 *
 * Project: Embedme
 * Author : FergusZeng
 * Email  : cblock@126.com
 * git	  : https://git.oschina.net/cblock/embedme
 * Copyright 2014~2020 @ ShenZhen ,China
*******************************************************************************/
#include "Tracer.h"
#include "XmlData.h"

namespace libemb{
using namespace std;
/* TinyXml说明:
 * xml文档中所有的东西都抽象成了一个个节点TiXmlNode,文档本身也抽象成了节点.
 * TiXmlDocument    :   文档,继承自TiXmlNode,可看成是根结点.
 * TiXmlDeclaration :   XML文件头(声明),继承自TiXmlNode:  <?xml version=\"1.0\"  standalone='no' >
 * TiXmlComment     :   注释,继承自TiXmlNode:             <!-- Do I need a secure PDA? -->
 * TiXmlText        :   文本结点,继承自TiXmlNode:         <Text> this is a text node.</Text>
 * TiXmlAttribute   :   属性值,继承自TiXmlNode:           <Item attr1="01" attr2='02' />
 */

/**
 *  \brief  XmlNode构造函数
 *  \param  none
 *  \return none
 *  \note   XmlNode代表一个XML子结点
 */
XmlNode::XmlNode():
m_root(NULL),
m_name("")
{
}
/**
 *  \brief  XmlNode析构函数
 *  \param  none
 *  \return none
 */
XmlNode::~XmlNode()
{
}

/**
 *  \brief  判断结点是否是空结点
 *  \param  none
 *  \return false or true
 *  \note   none
 */
bool XmlNode::isNullNode()
{
    if (m_root==NULL || 
        m_name.empty())
    {
        return true;
    }
    return false;
}
/**
 *  \brief  串行化XmlNode成字符串
 *  \param  none
 *  \return 成功返回xml字符串,失败返回空字符串
 *  \note   none
 */
std::string XmlNode::serialize()
{
    if(!isNullNode())
    {
    }
    return "";
}

/**
 *  \brief  获取子结点的个数
 *  \param  none
 *  \return 子结点个数
 *  \note   none
 */
int XmlNode::childNum()
{
    if (isNullNode())
    {
        return 0;
    }
    TiXmlElement* elem;
    int i=0;
    for(elem=m_root->FirstChildElement();elem!=NULL; elem=elem->NextSiblingElement())
    {
        i++;
    }
    return i;
}

/**
 *  \brief  根据索引获取兄弟结点
 *  \param  idx  索引值
 *  \return 如果存在则返回index对应的兄弟结点;否则返回空结点
 *  \note   例:node["item"][1],表示获取第2个item结点,node["item"]与node["item"][0]是等价的.
 */
XmlNode XmlNode::operator[](int idx)
{
    if (idx>=0 && !isNullNode())
    {
        int i=0;
        TiXmlNode* tinyNode;
        for(tinyNode=m_root; tinyNode!=NULL ; tinyNode=tinyNode->NextSibling(m_name.c_str()))
        {
            if (i==idx)
            {
                XmlNode node;
                node.m_root = tinyNode;
                node.m_name = m_name;
                return node;  
            }
            i++;
        }
    }
    XmlNode node;
    //TRACE_DBG_CLASS("No Node[\"%s\"][%d]\n",m_name.c_str(),idx);
    return node;
}
/**
 *  \brief  根据名称获取子结点
 *  \param  name 结点名称
 *  \return 如果存在则返回第一个子结点;否则返回空结点
 *  \note   none
 */
XmlNode XmlNode::operator[](std::string name)
{
    if (!isNullNode())
    {
        TiXmlNode* tinyNode=m_root->FirstChild(name.c_str());
        if (tinyNode!=NULL)
        {
            XmlNode node;
            node.m_root = tinyNode;
            node.m_name = name;
            return node;
        }
    }
    XmlNode node;
    return node;
}
/**
 *  \brief  获取结点整数值
 *  \param  none
 *  \return 返回整数值
 *  \note   none
 */
int XmlNode::toInt()
{
    if (isNullNode())
    {
        return 0;
    }
    string value = toString();
    if (!value.empty())
    {
        return atof(value.c_str());
    }
    return 0;
}
/**
 *  \brief  获取结点浮点数值
 *  \param  none
 *  \return 返回浮点数值
 *  \note   none
 */
double XmlNode::toDouble()
{
    if (isNullNode())
    {
        return 0.0;
    }
    string value = toString();
    if (!value.empty())
    {
        return atof(value.c_str());
    }
    return 0.0;
}
/**
 *  \brief  获取结点字符串值
 *  \param  none
 *  \return 返回字符串值
 *  \note   none
 */
string XmlNode::toString()
{
    if (!isNullNode())
    {
        //TRACE_YELLOW("node[%s] type:%d\n",m_name.c_str(),m_root->Type());
        switch(m_root->Type())
        {
            case TiXmlNode::TINYXML_ELEMENT:
            {
                TiXmlElement* element = m_root->ToElement();
                const char* text = element->GetText();
                if (element!=NULL && text!=NULL)
                {
                    return string(text);
                }
            }
            case TiXmlNode::TINYXML_TEXT:
            {
                return "";
            }
            default:
                break;
        }
    }
    return "";
}
/**
 *  \brief  设置结点整数值
 *  \param  value 整数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setValue(const int& value)
{
    if (isNullNode())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%d",value);
    setValue(string(buf));
	return false;
}
/**
 *  \brief  设置结点浮点数值
 *  \param  value 浮点数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setValue(const double& value)
{
    if (isNullNode())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%lf",value);
    setValue(string(buf));
	return false;
}
/**
 *  \brief  设置结点字符串值
 *  \param  value 字符串值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setValue(const std::string& value)
{
    if (isNullNode())
    {
        return false;
    }
    TiXmlElement* elem = m_root->ToElement();
    TiXmlText text(value.c_str());
	elem->ClearThis();
    if(NULL==elem->InsertEndChild(text))
    {
        TRACE_ERR_CLASS("insert text error.\n");
        return false;
    }
    
	return false;
}

/**
 *  \brief  增加整数型子结点
 *  \param  name  结点名称
 *  \param  value 整数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::addSubNode(const std::string& name, const int& value)
{
    if (name.empty() || isNullNode())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%d",value);
    return addSubNode(name, string(buf));
}
/**
 *  \brief  增加浮点型子结点
 *  \param  name  结点名称
 *  \param  value 浮点数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::addSubNode(const std::string& name, const double& value)
{
    if (name.empty() || isNullNode())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%lf",value);
    return addSubNode(name, string(buf));
}
/**
 *  \brief  增加字符串型子结点
 *  \param  name  结点名称
 *  \param  value 字符串值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::addSubNode(const std::string& name, const std::string value)
{
    if (name.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement elem(name.c_str());
    if (!value.empty())
    {
        TiXmlText text(value.c_str());
        if(NULL==elem.InsertEndChild(text))
        {
            TRACE_ERR_CLASS("insert text error.\n");
            return false;
        }
    }
        
    if(NULL==m_root->InsertEndChild(elem))
    {
        TRACE_ERR_CLASS("insert node error.\n");
        return false;
    }
    return true;
}
/**
 *  \brief  获取结点整型属性值
 *  \param  attr 属性名称
 *  \param  value 整型属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::getAttribute(const std::string& attr, int& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        if(NULL!=elem->Attribute(attr.c_str(),&value))
        {
            return true;
        }
    }
    value=0;
    return false;
}
/**
 *  \brief  获取结点浮点型属性值
 *  \param  attr 属性名称
 *  \param  value 浮点型属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::getAttribute(const std::string& attr, double& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        if(NULL!=elem->Attribute(attr.c_str(),&value))
        {
            return true;
        }
    }
    value=0.0;
    return false;
}
/**
 *  \brief  获取结点字符串型属性值
 *  \param  attr 属性名称
 *  \param  value 字符串属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::getAttribute(const std::string& attr, std::string& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        const char* strVal=elem->Attribute(attr.c_str());
        if(strVal!=NULL)
        {
            value = string(strVal);
            return true;
        }
    }
    value="";
    return false;
}
/**
 *  \brief  设置结点整型属性值
 *  \param  attr 属性名称
 *  \param  value 整型属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setAttribute(const std::string& attr, const int& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        elem->SetAttribute(attr.c_str(),value);
        return true;
    }
    return false;
}
/**
 *  \brief  设置结点浮点型属性值
 *  \param  attr 属性名称
 *  \param  value 浮点型属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setAttribute(const std::string& attr, const double& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        elem->SetAttribute(attr.c_str(),value);
        return true;
    }
    return false;
}
/**
 *  \brief  设置获取结点字符串型属性值
 *  \param  attr 属性名称
 *  \param  value 字符串属性值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlNode::setAttribute(const std::string& attr, const std::string& value)
{
    if (attr.empty() || isNullNode())
    {
        return false;
    }
    TiXmlElement *elem=m_root->ToElement();
    if (elem!=NULL)
    {
        elem->SetAttribute(attr.c_str(),value.c_str());
        return true;
    }
    return false;
}

std::string XmlNode::text()
{
    return toString();
}
bool XmlNode::setText(const std::string& text)
{
    return setValue(text);
}

/**
 *  \brief  XmlData构造函数
 *  \param  none
 *  \return none
 *  \note   XmlData代表一份Xml数据文档
 */
XmlData::XmlData()
{
}
/**
 *  \brief  XmlData析构函数
 *  \param  none
 *  \return none
 *  \note   none
 */
XmlData::~XmlData()
{
}
/**
 *  \brief  读取XML字符串进行初始化
 *  \param  none
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::initWithDataString(const string & content)
{
    m_document.Parse(content.c_str());
    if (m_document.Error())
    {
        TRACE_ERR_CLASS("xml parsing error!\n");
        return false;
    }
    return true;
}
/**
 *  \brief  读取XML文件内容进行初始化
 *  \param  none
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::initWithContentOfFile(const string & fileName)
{
    if (fileName.empty())
    {
        return false;
    }
    return m_document.LoadFile(fileName.c_str());
}

/**
 *  \brief  写入JSON数据到文件中
 *  \param  none
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::saveAsFile(const string & fileName)
{
    if (fileName.empty())
    {
        return false;
    }
    return m_document.SaveFile(fileName.c_str());
}
/**
 *  \brief  串行化XmlData成字符串
 *  \param  none
 *  \return 成功返回xml字符串,失败返回空字符串
 *  \note   none
 */
std::string XmlData::serialize()
{
    TiXmlPrinter printer;
	if(!m_document.Accept(&printer))
    {
        return "";
    }   
	const char* str=printer.CStr();
    if (str==NULL)
    {
        return "";
    }
    return string(str);
}
/**
 *  \brief  增加整数型子结点
 *  \param  name  结点名称
 *  \param  value 整数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::addSubNode(const std::string& nodeName,const int& value)
{
    if(nodeName.empty())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%d",value);
    return addSubNode(nodeName,string(buf));
}
/**
 *  \brief  增加浮点型子结点
 *  \param  name  结点名称
 *  \param  value 浮点数值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::addSubNode(const std::string& nodeName,const double& value)
{
    if(nodeName.empty())
    {
        return false;
    }
    char buf[32]={0};
    sprintf(buf,"%lf",value);
    return addSubNode(nodeName,string(buf));
}

/**
 *  \brief  增加字符串型子结点
 *  \param  name  结点名称
 *  \param  value 字符串值
 *  \return 成功返回true,失败返回false
 *  \note   none
 */
bool XmlData::addSubNode(const std::string& nodeName,const std::string value)
{
    if(nodeName.empty())
    {
        return false;
    }
    TiXmlElement elem(nodeName.c_str());  
    if(NULL==m_document.InsertEndChild(elem))
    {
        TRACE_ERR_CLASS("insert node error.\n");
        return false;
    }
    if(!(*this)[nodeName.c_str()].setValue(value))
    {
        return false;
    }
    return true;
}
/**
 *  \brief  根据名称获取子结点
 *  \param  name 结点名称
 *  \return 如果存在则返回第一个子结点;否则返回空结点
 *  \note   none
 */
XmlNode XmlData::operator[](string name)
{
    if (!name.empty())
    {
        TiXmlNode* tinyNode = m_document.FirstChild(name.c_str());
        if (tinyNode!=NULL)
        {
            XmlNode node;
            node.m_root=tinyNode;
            node.m_name=name;
            //TRACE_DBG_CLASS("Node[\"%s\"],type=%d\n",name.c_str(),tinyNode->Type());
            return node;
        }
    }
    //TRACE_DBG_CLASS("No Node[\"%s\"]\n",name.c_str());
    XmlNode node;
    return node;
}
}
